﻿using MongoDB.Driver;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text;
using System.Threading.Tasks;
using ZYHand.Entity;
using ZYHand.IService;
using ZYHand.Repository;
using ZYHand.Service;
using ZYHand.Utility;

namespace ZYHand.Consumer.ToMongoDB
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var connection = RabbitMQClient.GetInstance().CreateConnection())//连接服务器，即正在创建终结点。
            {
                //创建一个通道，这个就是Rabbit自己定义的规则了，如果自己写消息队列，这个就可以开脑洞设计了
                //这里Rabbit的玩法就是一个通道channel下包含多个队列Queue
                using (var channel = connection.CreateModel())
                {
                    var response = channel.QueueDeclare("Search", true, false, false, null);
                    uint sumCount = response.MessageCount;
                    Console.WriteLine("当前消息数量：" + sumCount + ",正在处理中");  //获取等待处理的队列数

                    /* 这里定义了一个消费者，用于消费服务器接受的消息
                     * C#开发需要注意下这里，在一些非面向对象和面向对象比较差的语言中，是非常重视这种设计模式的。
                     * 比如RabbitMQ使用了生产者与消费者模式，然后很多相关的使用文章都在拿这个生产者和消费者来表述。
                     * 但是，在C#里，生产者与消费者对我们而言，根本算不上一种设计模式，他就是一种最基础的代码编写规则。
                     * 所以，大家不要复杂的名词吓到，其实，并没那么复杂。
                     * 这里，其实就是定义一个EventingBasicConsumer类型的对象，然后该对象有个Received事件，该事件会在服务接收到数据时触发。
                     */
                    var consumer = new EventingBasicConsumer(channel);//消费者 

                    //一个消费者开多线程，但一个Task线程只绑定一个queue，当对同一份数据做不同操作时，能保证数据的先后顺序
                    var searchTask = Task.Run(() =>
                    {
                        //0.消费指定rabbitMQ中的Queue数据，并设置不自动应答
                        channel.BasicConsume("Search", false, consumer);//消费消息 autoAck参数为消费后是否删除,使用false不自动确认；true时会把队列消息全部标记为消费掉，其实客户端可能还没处理完成会照成数据丢失
                        consumer.Received += (model, ea) =>
                        {
                            var body = ea.Body;
                            var message = Encoding.UTF8.GetString(body);
                            var publishData = JsonConvert.DeserializeObject<JObject>(message); //内部Data为未知对象，先反序列化为JObject对象，可在下一步明确对象类型
                            var dataType = publishData.GetValue("Type").ToString();

                            //插入提现审核流数据
                            if (dataType == EnumHelper.RabbitMQType.InsertTakeCashApply.ToString())
                            {

 #region 插入信息
                                //1.获取指令包中的Data参数信息
                                var data = publishData.GetValue("Data").ToString();

                                //2.根据Data参数去sqlserver中查询相关数据后再处理
                                //......

                                //3.接收mq的数据 
                                //使用mongodb接收数据

                                IMongoDBService mongoDBService = new MongoDBService();
                                bool isPost = mongoDBService.AddModelbyString("TakeCashApply", data) == 1 ? true : false;

                                //4.处理完数据后，需要给rabbitMQ发送ack应答，以便删除对应队列中的数据
                                if (isPost)
                                {
                                    channel.BasicAck(ea.DeliveryTag, false);  //手动ack应答，可以解决消费者处理不及时的消息，重新回到mq队列中！！！
                                    Console.WriteLine("\r当前消息数量：" + (sumCount==0? sumCount:--sumCount) + ",正在处理中"); // Console.SetCursorPosition(0, 0); //重置光标起点
                                }
                                else
                                {
                                    Console.WriteLine("处理失败");
                                }
#endregion 
                            }
                            else if (dataType == EnumHelper.RabbitMQType.DeleteOrder.ToString())
                            {
  #region 删除信息
                                //1.获取指令包中的Data参数信息
                                var data = publishData.GetValue("Data")["orderID"].ToString();
                                if (string.IsNullOrEmpty(data))
                                {
                                    Console.WriteLine("删除条件不能为空");
                                    channel.BasicAck(ea.DeliveryTag, false);  //删除条件为空，queue信息不合格要清除。手动ack应答，可以解决消费者处理不及时的消息，重新回到mq队列中！！！
                                }

                                //2.根据Data参数去sqlserver中查询相关数据后再处理
                                //......

                                //3.接收数据 
                                //使用mongodb接收数据
                                var list = new List<FilterDefinition<ExpandoObject>>();
                                list.Add(Builders<ExpandoObject>.Filter.Regex("orderID", data));//查询条件2
                                FilterDefinition<ExpandoObject> filter = Builders<ExpandoObject>.Filter.And(list);   //Or查询，And查询

                                IMongoDBService mongoDBService = new MongoDBService();
                                var deleteCount = mongoDBService.DeleteMany("Order", filter);

                                //4.处理完数据后，需要给rabbitMQ发送ack应答，以便删除对应队列中的数据
                                if (deleteCount > 0)
                                {
                                    channel.BasicAck(ea.DeliveryTag, false);  //手动ack应答，可以解决消费者处理不及时的消息，重新回到mq队列中！！！
                                    Console.WriteLine("\r当前消息数量：" + (--sumCount) + ",正在处理中");   // Console.SetCursorPosition(0, 0); //重置光标起点
                                }
                                else
                                {
                                    Console.WriteLine("处理失败");
                                }
#endregion
                            }
                            else if (dataType == EnumHelper.RabbitMQType.UpdateOrder.ToString())
                            {
 #region 修改信息
 #endregion 
                            }
                        };
                    });
                    Console.ReadLine();
                }
            }
        }
    }
}
